<?php

namespace System\library\loader;

/**
 * <b>Class</b> :        Javascript<br>
 * <b>Fonction</b> :     Permet faire un js global et de le mettre en cache
 * <br>
 * @version              1.0
 * @author               Steuf
 * @version              2.0<br>
 *                       Ajout de la phpdoc<br>
 *                       Typo correction
 * <br>
 * @author               Dread <dreadlokeur@gmail.com>
 * @license              Licence publique générale GNU http://www.gnu.org/licenses/gpl.html
 * @copyright            Copyright 2011 - MidichloriansPHP and contributors
 * @package              System
 * @subpackage           library\loader
 */
class JavascriptLoader {

    /**
     * L'attribut qui stock si l'on doit compresser le js
     *
     * @access private
     * @var bool
     */
    private $_compress = false;
    /**
     * L'attribut qui stock si l'on doit mettre en cache le js
     *
     * @access private
     * @var bool
     */
    private $_cached = true;
    /**
     * L'attribut qui stock le nom du repertoire de cache
     *
     * @access private
     * @var string
     */
    private $_cacheDir = '';
    /**
     * L'attribut qui stock le nom du javascript de sortie
     *
     * @access private
     * @var string
     */
    private $_cacheName = 'javascript.js';
    /**
     * L'attribut qui stock le nom du hash generer, lors de la mise en cache du javascript
     *
     * @access private
     * @var string
     */
    private $_cacheNameHash = 'javascript_hash';
    /**
     * L'attribut qui stock les divers javascripts à stocker dans le js de fin
     *
     * @access private
     * @var array
     */
    private $_files = array();
    /**
     * L'attribut qui stock l'information: on force l'update du cache ou pas
     *
     * @access private
     * @var array
     */
    private $_forceCacheUpdate = false;

    /**
     * Permet de définir si on va compresser le js de fin ou pas
     *
     * @access public
     * @param bool $bool compression: oui ou non
     * @return void
     */
    public function setCompressed($bool) {
        if (!is_bool($bool))
            throw new \Exception('Compress parameter must be a boolean');
        $this->_compress = (bool) $bool;
        return $this;
    }

    /**
     * Permet de définir si on va mettre en cache le js de fin ou pas
     *
     * @access public
     * @param bool $bool en cache oui ou non
     * @return void
     */
    public function setCached($bool) {
        if (!is_bool($bool))
            throw new \Exception('Cached parameter must be a boolean');
        $this->_cached = (bool) $bool;
        return $this;
    }

    /**
     * Permet de définir le nom du js mit en cache
     *
     * @access public
     * @param string $cacheName nom du js en cache
     * @return void
     */
    public function setCacheName($cacheName) {
        if (!is_string($cacheName))
            throw new \Exception('cacheName parameter must be a string');
        $this->_cacheName = $cacheName;
        return $this;
    }

    /**
     * Permet de définir le nom du hash de cache du js
     *
     * @access public
     * @param string $cacheNameHash le nom du hash de cache du js
     * @return void
     */
    public function setCacheHashName($cacheNameHash) {
        if (!is_string($cacheNameHash))
            throw new \Exception('cacheNameHash parameter must be a string');
        $this->_cacheNameHash = $cacheNameHash;
        return $this;
    }

    /**
     * Permet de définir si l'on veut forcer la mise à jour du cache
     *
     * @access public
     * @param bool $bool oui, non
     * @return void
     */
    public function setForceCacheUpdate($bool) {
        if (!is_string($bool))
            throw new \Exception('forceCacheUpdate parameter must be a boolean');
        $this->_forceCacheUpdate = (bool) $bool;
        return $this;
    }

    /**
     * Permet de définir le répertoire de mise en cache du js
     *
     * @access public
     * @param string $directory le répertoire de cache
     * @return void
     */
    public function setCacheDir($directory) {
        if (!is_dir($directory))
            throw new \Exception('Directory ' . $directory . ' don\'t exist');
        if (!is_writable($directory))
            throw new \Exception('Directory ' . $directory . ' is not writtable');

        $this->_cacheDir = $directory;
        return $this;
    }

    /**
     * Permet d'ajouter un fichier javascript au js final
     *
     * @access public
     * @param string $file le fichier javascript à ajouter au fichier js final
     * @param bool $alreadyCompressed javascript déja compressé?
     * @return void
     */
    public function addFile($file, $alreadyCompressed = false) {
        if (!file_exists($file) || !is_file($file))
            throw new \Exception('File ' . $file . ' don\'t exist');
        if (!is_bool($alreadyCompressed))
            throw new \Exception('alreadyCompressed parameter must be a boolean');

        $this->_files[] = array(
            'name' => $file,
            'lastUpdate' => filemtime($file),
            'alreadyCompressed' => (bool) $alreadyCompressed
        );
    }

    /**
     * Permet d'obtenir le javascript final mit en cache et compressé (si demandé)
     *
     * @access public
     * @param void
     * @return void
     */
    public function output() {
        //Si on le met en cache
        if ($this->_cached) {
            if (!is_dir($this->_cacheDir))
                throw new \Exception('Cache directory not setted');
            //Cache exipré, ou demande de mise à jour d cache forcé: on genere le cache
            if ($this->_cacheExpired() || $this->_forceCacheUpdate)
                $this->_generateCache();
        }else
            echo $this->_getContent();
    }

    /**
     * Permet de regarder si le cache du javascript final est expiré: un fichier js à été modifié, ou le cache n'existe pas encore...
     *
     * @access private
     * @param void
     * @return bool true si le cache est expiré: hash différent, cache vide...
     */
    private function _cacheExpired() {
        if (!file_exists($this->_cacheDir . $this->_cacheName) || !file_exists($this->_cacheDir . $this->_cacheNameHash))
            return true;
        // Check Hash liste files
        $hash = file_get_contents($this->_cacheDir . $this->_cacheNameHash);
        if ($hash != md5(serialize($this->_files)))
            return true;
        // Check file updated
        $cacheDate = filemtime($this->_cacheDir . $this->_cacheName);
        foreach ($this->_files as $file) {
            if ($cacheDate < $file['lastUpdate'])
                return true;
        }
    }

    /**
     * Permet de generer le cache
     *
     * @access private
     * @param void
     * @return void
     */
    private function _generateCache() {
        file_put_contents($this->_cacheDir . $this->_cacheName, $this->_getContent());
        file_put_contents($this->_cacheDir . $this->_cacheNameHash, md5(serialize($this->_files)));
    }

    /**
     * Permet d'obtenir le javascript final: cumul de tous les javascript ajoutés
     *
     * @access private
     * @param void
     * @return string le contenu du javascript final
     */
    private function _getContent() {
        $notCompressed = $content = '';
        //On parcours les fichiers javascripts ajoutés
        foreach ($this->_files as $file) {
            //On stock le contenu
            $js = file_get_contents($file['name']);
            //On compresse, si l'option est demandé
            if ($this->_compress && !$file['alreadyCompressed']) {
                // Compress file with Javascript Packer plugin
                $packer = new \JavaScriptPacker($js);
                $notCompressed .= trim($packer->pack());
            }else
                $content .= $js;

            if (substr($notCompressed, -1) != ';')
                $notCompressed .= ";";
        }
        return $content . $notCompressed;
    }

}

?>